package com.wewins.fota.service;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wewins.fota.aop.Operator;
import com.wewins.fota.bean.bo.filter.FilterCond;
import com.wewins.fota.bean.dto.*;
import com.wewins.fota.bean.po.FotaLog;
import com.wewins.fota.bean.po.UpdatePlan;
import com.wewins.fota.common.constant.CommonConst;
import com.wewins.fota.common.constant.FotaCode;
import com.wewins.fota.common.constant.HttpConst;
import com.wewins.fota.common.constant.RedisConst;
import com.wewins.fota.mapper.PkgVersionMapper;
import com.wewins.fota.mapper.UpdatePlanMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PlanService implements BasicService<UpdatePlan, UpdatePlan> {
    private final UpdatePlanMapper mapper;
    private final PkgVersionMapper pkgVersionMapper;
    private final RedisCacheService redisCacheService;
    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public PlanService(
            UpdatePlanMapper mapper,
            PkgVersionMapper pkgVersionMapper,
            RedisCacheService redisCacheService,
            RedisTemplate<String, Object> redisTemplate) {
        this.mapper = mapper;
        this.pkgVersionMapper = pkgVersionMapper;
        this.redisCacheService = redisCacheService;
        this.redisTemplate = redisTemplate;
    }

    public <K extends FilterCond> PageRst<UpdatePlan> queryAll(PageReq<K> pageReq) {
        PageHelper.startPage(pageReq);
        return PageRst.<UpdatePlan>builder().build().fillBy(PageInfo.of(mapper.queryAll(pageReq.getFilterBy())));
    }

    @Transactional(rollbackFor = Exception.class)
    public <K extends FilterCond> PageRst<UpdatePlanDetails> queryAllDetails(PageReq<K> pageReq) {
        PageHelper.startPage(pageReq);
        List<UpdatePlan> plans = mapper.queryAll(pageReq.getFilterBy());
        List<UpdatePlanDetails> planDetails = plans.stream().map(item -> UpdatePlanDetails.builder()
                .srcVersionList(pkgVersionMapper.findAllByProductVersions(item.getProduct(), item.parseSrcVersions()))
                .dstVersion(pkgVersionMapper.findByProductVersion(item.getProduct(), item.getDstV()))
                .build().fillBy(item)).collect(Collectors.toList());
        return PageRst.<UpdatePlanDetails>builder().build().fillBy(PageInfo.of(planDetails));
    }

    @Override
    public UpdatePlan findById(long id) {
        return mapper.findById((int) id);
    }

    @Transactional(rollbackFor = Exception.class)
    public UpdatePlanDetails findDetailById(long id) {
        UpdatePlan plan = this.findById(id);
        return UpdatePlanDetails.builder()
                .srcVersionList(pkgVersionMapper.findAllByProductVersions(plan.getProduct(), plan.parseSrcVersions()))
                .dstVersion(pkgVersionMapper.findByProductVersion(plan.getProduct(), plan.getDstV()))
                .build().fillBy(plan);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @Operator(FotaCode.CMD_ADD_PLAN)
    public UpdatePlan insert(String username, UpdatePlan data) {
        data.setCreateTime(System.currentTimeMillis());
        data.refreshUser(username);
        mapper.insert(data);
        redisCacheService.cacheOnePlan(data);
        return data;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @Operator(FotaCode.CMD_UPDATE_PLAN)
    public Integer update(String username, UpdatePlan data) {
        data.setUpdateTime(System.currentTimeMillis());
        data.refreshUser(username);
        int count = mapper.update(data);
        redisCacheService.removeOldCache4PlanById(data.getId());
        redisCacheService.cacheOnePlan(mapper.findById(data.getId()));
        return count;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @Operator(FotaCode.CMD_DELETE_PLAN)
    public Integer deleteById(String username, long id) {
        UpdatePlan plan = this.findById(id);
        if (plan == null) {
            return 0;
        }
        log.info("current plan is:" + plan + ", username:" + username);
        redisCacheService.removeOldCache4Plan(plan);
        return mapper.delete(plan);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer delete(String username, UpdatePlan data) {
        return this.deleteById(username, data.getId());
    }

    /**
     * 查询走redis, 日志写redis，后台线程写数据库
     */
    public CheckStatusResp checkStatus(HttpServletRequest request, CheckStatusReq req) {
        CheckStatusResp resp = redisCacheService.checkStatus(req);
        String logStr = buildFotaLogStr(request, req, resp == null ? "-" : resp.getVersion());
        this.redisTemplate.opsForList().leftPush(RedisConst.REDIS_KEY_LOG_LIST, logStr);
        return resp;
    }


    private String buildFotaLogStr(HttpServletRequest request, CheckStatusReq req, String dstV) {
        String reqServer = request.getHeader(HttpConst.HEADER_KEY_REQUEST_SERVER_IP);
        String respServer = request.getHeader(HttpConst.HEADER_KEY_RESPONSE_SERVER_IP);
        FotaLog fotaLog = FotaLog.builder()
                .product(req.getProduct())
                .imei(req.getImei())
                .reqServer(StringUtils.isEmpty(reqServer) ? "" : reqServer)
                .respServer(StringUtils.isEmpty(respServer) ? "" : respServer)
                .operator(CommonConst.LOG_OPERATE_QUERY)
                .autoDownload(req.getAuto())
                .fromV(req.getVersion())
                .toV(dstV)
                .createTime(System.currentTimeMillis())
                .count(1)
                .build();

        return JSON.toJSONString(fotaLog);
    }

    public List<String> queryNames(String subName) {
        Set<String> keys = redisTemplate.keys(String.format(RedisConst.REDIS_KEY_PLAN_MAP_BY_NAME, "*" + subName + "*"));
        if (keys == null) {
            return new ArrayList<>();
        }
        return keys.stream().map(
                item -> (String) redisTemplate.opsForHash().get((String) redisTemplate.opsForValue().get(item), "name")
        ).distinct().collect(Collectors.toList());
    }

    public Boolean existPlanByName(String planName) {
        return redisTemplate.hasKey(String.format(RedisConst.REDIS_KEY_PLAN_MAP_BY_NAME, planName));
    }
}
